home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / SAT 2.3.8 / Demos / Collision ⁄⁄⁄ demo ƒ / Collision ⁄⁄⁄.p < prev    next >
Text File  |  1996-05-23  |  38KB  |  1,309 lines

  1. {Collision ///}
  2. {}
  3. {This demo demonstrates some alternative ways to use SAT:}
  4. {}
  5. {• The animation is called from the standard event loop (via TransSkel). This slows things down}
  6. {quite a bit (since all other processes are allowed to run), but makes the application background-}
  7. {friendly.}
  8. {• It runs in an ordinary, moveable window. We can, with some effort, do this while still}
  9. {using the fast mode (in which case we would have to restrict window dragging so it stays}
  10. {within the main screen, modify certain global (gSAT.ox ad gSAT.oy), and also limit the}
  11. {horizontal alignment of the window like HyperCard does), but in this demo we just use the}
  12. {slow (safe) mode. }
  13. {• We create sprites from QuickDraw calls instead of cicns.}
  14. {• We use the mask regions of the sprites for better collision detection.}
  15. {• We use a pattern as backkground instead of PICTs.}
  16. {• All the code is in one unit. This might make it less structured, less encapsulated, but I wanted}
  17. {to show you that you don't have to do things exactly the way I do in the other demos.}
  18. {• Using a modified sprite record.}
  19. {• Fixed-point positions}
  20. {}
  21. {However, some variations remain that aren't demonstrated even here:}
  22. {• Calculating the positions of the sprites with the system clock (TickCount or Time Manager) instead of}
  23. {moving them each frame. That approach has some advantages (i.e. constant speed on objects), can easily}
  24. {be used with SAT, but is not as simple.}
  25.  
  26.  
  27. program CollisionIII;
  28.  
  29.     uses
  30. {$ifc UNDEFINED THINK_PASCAL}
  31.         Types, QuickDraw, Events, Windows, Dialogs, Fonts, DiskInit, TextEdit,{}
  32.         Traps, Memory, SegLoad, Scrap, ToolUtils, OSUtils, Menus, Resources,{}
  33.         StandardFile, GestaltEqu, Files, Errors, Controls, TextUtils, QuickDrawText,
  34. {$elsec}
  35.         InterfacesUI,  {To give Think Pascal some UPI}
  36. {$endc}
  37.         TransSkel, SAT;
  38.  
  39. {A modified sprite record. This should usually go in some globals unit}
  40. {in your project.}
  41. type
  42.         C3SpritePtr = ^C3Sprite;
  43.         C3Sprite = record
  44. { Variables that you should change as appropriate }
  45.                 kind: Integer; { Used for identification. >0: friend. <0 foe }
  46.                 position: Point;
  47.                 hotRect, hotRect2: Rect; { Tells how large the sprite is; hotRect is centered around origo }
  48.                                         {hotRect is set by you. hotRect2 is offset to the current position.}
  49.                 face: FacePtr; { Pointer to the Face (appearance) to be used. }
  50.                 task: ProcPtr; { Callback-routine, called once per frame. If task=nil, the sprite is removed. }
  51.                 hitTask: ProcPtr; { Callback in collisions. }
  52.                 destructTask: ProcPtr; { Called when a sprite is disposed. (Usually nil.) }
  53.                 clip: RgnHandle;
  54. { SAT variables that you shouldn't change: }
  55.                 oldpos: Point;                {The 'task' routine is not allowed to change this! }
  56.                 next, prev: SpritePtr;    {You may change them in your own sorting routine, but be careful if you do.}
  57.                 r, oldr: Rect;                {Rectangle telling where to draw. Avoid messing with it.}
  58.                 oldFace: FacePtr;            {Used by RunSAT2}
  59.                 dirty: Boolean;            {Used by RunSAT2}
  60. {Variables for internal use by the sprites. I have edited them, to add fixed-point postions!}
  61. {Since we have edited the record, we must SetSpriteSize immediately}
  62. {after initializing (before any sprites are created)!}
  63.                 layer: integer; {For layer-sorting. When not used for that, use freely.}
  64.                 speed: Point; { Can be used for speed, but not necessarily. }
  65.                 mode: integer; { Usually used for different modes and/or to determine what image to show next. }
  66.                 fixedPos: Point; {Position * 16}
  67.             end;
  68.  
  69.     const
  70.         newgameItem = 1;
  71.         clearHighItem = 4;
  72.  
  73.         aboutAlrt = 128;
  74.         fileMenuRes = 128;
  75.         shapeMenuRes = 129;
  76.         theWindRes = 128;
  77.  
  78.         kGameTime = 3600; {60 sekunder}
  79.         kExtraTime = 180; {3 sekunder}
  80.         kLevelBonus = 25;
  81.  
  82.     type
  83.         SettingsRec = record
  84.                 high: Longint;
  85.                 player: string[5];
  86.             end;
  87.         SettingsPtr = ^SettingsRec;
  88.         SettingsHnd = ^SettingsPtr;
  89.     var
  90.         settings: SettingsHnd;
  91.         fileMenu, shapeMenu: MenuHandle;
  92.         gameRunning: Boolean;
  93.         gameStartTime, lastSetStartTime: Longint;
  94.         setCount: integer;
  95.         gMode: integer;
  96.         scoreFace, highFace, lastface: FacePtr;
  97.         myFace, welcomeFace: FacePtr;
  98.         score: Longint;
  99.         bgPat: SATPatHandle;
  100.  
  101.         scaledFace: array[0..31] of FacePtr;
  102.  
  103.     procedure Barf;
  104.     begin
  105.         SATReportStr('Something went wrong. Sorry.');
  106.         halt;
  107.     end;
  108.  
  109. {Ljud:}
  110.  
  111. {Konstruera en snd-resurs artificiellt}
  112. {Rutinen bygger en handle med reserverad plats för ljudet, som sedan värdrutinen kan skapa.}
  113.     function CreateSnd (size: longint; var sndH: handle; var dataPek: Ptr): Boolean;
  114.         type
  115.             mySndRec = packed record
  116.                     format: integer;
  117.                     numsynth: integer; {must be 0}
  118. {synth}
  119.                     synthid: integer;{5}
  120.                     synthinit: longint;{0}
  121.  
  122.                     numcom: integer; {must be 1}
  123. {command}
  124.                     command: integer;{ $8051}
  125.                     param1: integer; {0}
  126.                     param2: longint; { $14}
  127. {sound header}
  128.                     dataptr: Ptr;
  129.                     datasize: longint;
  130.                     samplerate: longint; {22kHz = $56ee8ba3}
  131.                     loopstart: Ptr;
  132.                     loopend: Ptr;
  133.                     encoding: Byte;{0}
  134.                     basenote: Byte; { $3c}
  135. {data}
  136.                     ljud: packed array[0..0] of Byte;
  137.                 end;
  138.             msrp = ^mySndRec;
  139.             msrh = ^msrp;
  140.         var
  141.             h: msrh;
  142.     begin
  143.         h := msrh(NewHandle(sizeof(mySndRec) + size));
  144.         if h = nil then
  145.             CreateSnd := false
  146.         else
  147.             begin
  148.                 HLock(Handle(h)); {Fixar detta buggen med att ljuden ändras?}
  149.                 with h^^ do
  150.                     begin
  151.                         format := 1;
  152.                         numsynth := 1;
  153.                         synthid := 5;
  154.                         synthinit := 0;
  155.                         numcom := 1;
  156.                         command := $8051;
  157.                         param1 := 0;
  158.                         param2 := $14;
  159.                         dataptr := @ljud[0];
  160.                         datasize := size;
  161.                         samplerate := $56ee8ba3; {div 2 - fast varför köra 11kHz när man synthar?!}
  162.                         loopstart := dataptr;
  163.                         loopend := dataptr; {?}
  164.                         encoding := 0;
  165.                         basenote := $3c;
  166.                         dataPek := dataptr; {Utdata}
  167.                     end; {with}
  168.                 SndH := handle(h);{utdata}
  169.                 CreateSnd := true;
  170.             end; {if nil else}
  171.     end;{CreateSnd}
  172.  
  173.     var
  174.         pushH, bippH, baeH: Handle;
  175.  
  176. {Fixa några bra subrutiner för ljudsyntning?!}
  177. {- Eko}
  178. {- Lågpass och högpass}
  179. {- Sampla upp eller ner?}
  180. {- Frekvensvariation?}
  181. {- Fade in, fade out (mm envelope)}
  182. {Drömmen är förstås FFT, så man kan göra riktigt vass bandspärr, frekvensskift mm.}
  183. {Apropå: kan man inte göra bra ljudkompression med FFT?}
  184.  
  185. {$PUSH}
  186. {$R-}
  187.  
  188. {Rutinen som skall bygga de syntetiska ljud vi önskar!}
  189.     procedure Synth;
  190.         type
  191.             ArtRec = record
  192.                     arr: packed array[0..10000] of Byte;
  193.                 end;
  194.             ArtPtr = ^ArtRec;
  195.         var
  196.             tmpPtr: ArtPtr;
  197.             i: integer;
  198.         const
  199.             pushSize = 3479;
  200.             bippSize = 2959;
  201.             baeSize = 20000;
  202.     begin
  203.         if not CreateSnd(pushSize + 1, pushH, Ptr(tmpptr)) then
  204.             CheckNoMem(nil); {EmergencyExit}
  205.         for i := 0 to pushSize do
  206.             tmpptr^.arr[i] := band(char(random), 127) * (pushSize - i) div pushSize + 128;
  207.         for i := 0 to pushSize - 3 do
  208.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  209.         for i := 0 to 64 do
  210.             begin
  211. {tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;}
  212.                 tmpptr^.arr[pushSize - i] := tmpptr^.arr[pushSize - i] * i div 64;
  213.             end;
  214.  
  215.         if not CreateSnd(bippSize + 1, bippH, Ptr(tmpptr)) then
  216.             CheckNoMem(nil); {EmergencyExit}
  217.         for i := 0 to bippSize do
  218.             tmpptr^.arr[i] := i mod (i div 171 + 1) mod 127 + 128; {mjiioo}
  219. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  220. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  221. {tmpptr^.arr[i] := band(i, 63) + 128;}
  222.         for i := 0 to 64 do
  223.             begin
  224.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  225.                 tmpptr^.arr[bippSize - i] := tmpptr^.arr[bippSize - i] * i div 64;
  226.             end;
  227.  
  228.         if not CreateSnd(baeSize + 1, baeH, Ptr(tmpptr)) then
  229.             CheckNoMem(nil); {EmergencyExit}
  230.         for i := 0 to baeSize do
  231.             tmpptr^.arr[i] := (i div 5) mod (i div 1571 + 1) mod 127 + 128; {mjiioo}
  232. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  233. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  234. {tmpptr^.arr[i] := band(i, 63) + 128;}
  235.         for i := 0 to baeSize - 3 do
  236.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  237.         for i := 0 to 64 do
  238.             begin
  239.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  240.                 tmpptr^.arr[baeSize - i] := tmpptr^.arr[baeSize - i] * i div 64;
  241.             end;
  242.     end;
  243. {$POP}
  244.  
  245.     procedure DoAbout;
  246.     begin
  247.         if 1 = Alert(aboutAlrt, nil) then
  248.             ;
  249.     end;
  250.  
  251. {Two handly routines from my dialog utilities unit.}
  252.     procedure SetTextDItem (theDialog: DialogPtr; itemNo: integer; theString: Str255);
  253.         var
  254.             kind: integer;
  255.             item: ControlHandle;
  256.             box: Rect;
  257.     begin
  258.         GetDialogItem(theDialog, itemNo, kind, Handle(item), box);
  259. {Check kind}
  260.         kind := BitAnd(kind, 127);
  261.         case kind of
  262.             8, 16: {statText, editText}
  263.                 SetDialogItemText(handle(item), theString);
  264.             0, 1, 2, 4, 5, 6: {button, checkbox, radio - men vad är 4?}
  265.                 SetControlTitle(item, theString);
  266.             otherwise {Övriga har ingen text man kan sätta}
  267.                 SysBeep(1);
  268.         end;{case}
  269.     end;
  270.     function GetTextDItem (theDialog: DialogPtr; itemNo: integer): Str255;
  271.         var
  272.             kind: integer;
  273.             item: ControlHandle;
  274.             box: Rect;
  275.             tmpStr: Str255;
  276.     begin
  277.         GetDialogItem(theDialog, itemNo, kind, Handle(item), box);
  278. {Check kind}
  279.         kind := BitAnd(kind, 127);
  280.         tmpStr := '';
  281.         case kind of
  282.             8, 16: {statText, editText}
  283.                 GetDialogItemText(handle(item), tmpStr);
  284.             0, 1, 2, 4, 5, 6: {button, checkbox, radio…?}
  285.                 GetControlTitle(item, tmpStr);
  286.             otherwise {Övriga har ingen text man kan sätta}
  287.                 SysBeep(1);
  288.         end;{case}
  289.         GetTextDItem := tmpStr;
  290.     end;
  291.     function MyNumToString (l: longint): Str255;
  292.         var
  293.             tmpStr: Str255;
  294.     begin
  295.         NumToString(l, tmpStr);
  296.         MyNumToString := tmpStr;
  297.     end;
  298.  
  299. {Make the new high score dialog}
  300.     procedure AskHigh;
  301.         const
  302.             highDlogID = 129;
  303.         var
  304.             dialog: DialogPtr;
  305.             oldPort: GrafPtr;
  306.             itemHit: integer;
  307.             str: str255;
  308.     begin
  309.         GetPort(oldPort);
  310.         dialog := GetNewDialog(highDlogID, nil, WindowPtr(-1));
  311.         ShowWindow(dialog);
  312.         SelectWindow(dialog);
  313.         SetPort(dialog);
  314.  
  315.         SetTextDItem(dialog, 3, settings^^.player);
  316.         SelectDialogItemText(dialog, 3, 0, 32767);
  317.         itemHit := -1;
  318.         while (itemHit <> 1) and (itemHit <> 2) do { 1=ok, 2=cancel }
  319.             ModalDialog(nil, itemHit);
  320.         if itemHit = 1 then
  321.             begin
  322.                 str := GetTextDItem(dialog, 3);
  323.                 if length(str) > 5 then
  324.                     str[0] := char(5); {snabbaste sättet att korta den!}
  325.                 settings^^.player := str;
  326.                 settings^^.high := score;
  327.             end;
  328.         DisposeDialog(dialog);
  329.         SetPort(oldPort);
  330.     end;
  331.  
  332. {Reuseable sprite movement routine, called from all sprite handling routines. Some sprites use this}
  333. {as handling routine.}
  334.     procedure SATBounce (me: SpritePtr);
  335.     begin
  336.         me^.position.h := me^.position.h + me^.speed.h;
  337.         me^.position.v := me^.position.v + me^.speed.v;
  338.         if me^.position.h < 0 then
  339.             me^.speed.h := abs(me^.speed.h);
  340.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  341.             me^.speed.h := -abs(me^.speed.h);
  342.         if me^.position.v < 0 then
  343.             me^.speed.v := abs(me^.speed.v);
  344.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  345.             me^.speed.v := -abs(me^.speed.v);
  346.     end;
  347.  
  348. {The same but using fixed-point position, as in HandlePlayer}
  349.     procedure SATFixedBounce (me: C3SpritePtr);
  350.     begin
  351.         me^.fixedPos.h := me^.fixedPos.h + me^.speed.h;
  352.         me^.fixedPos.v := me^.fixedPos.v + me^.speed.v;
  353.  
  354.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  355.         me^.position.v := BSR(me^.fixedPos.v, 4);
  356.  
  357. {Since BSR isn't aritmetic shift, a negative fixedPos will unfortunately result in}
  358. {a very large positive position. This must be accounted for when checking borders}
  359. {- or we could use div, but that is slower.}
  360.  
  361.         if me^.fixedPos.h < 0 then
  362.             begin
  363.                 me^.speed.h := abs(me^.speed.h);
  364.                 me^.position.h := 0;
  365.             end
  366.         else if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  367.             me^.speed.h := -abs(me^.speed.h);
  368.         if me^.fixedPos.v < 0 then
  369.             begin
  370.                 me^.speed.v := abs(me^.speed.v);
  371.                 me^.position.v := 0;
  372.             end
  373.         else if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  374.             me^.speed.v := -abs(me^.speed.v);
  375.     end;
  376.  
  377.  
  378.     procedure HandleTheSprite (me: C3SpritePtr);
  379.     begin
  380.         if me^.speed.h = 0 then
  381.             me^.speed.h := SATRand(32) - SATRand(32);
  382.         if me^.speed.v = 0 then
  383.             me^.speed.v := SATRand(32) - SATRand(32);
  384.         if me^.face = nil then
  385.             begin
  386.                 me^.face := myFace;
  387.                 if me^.face <> nil then
  388.                     me^.hotRect := me^.face^.iconMask.bounds;
  389.             end;
  390.         SATFixedBounce(me);
  391.     end;
  392.  
  393.     procedure RedrawScoreFace;
  394.     begin
  395.         SATSetPortFace(scoreFace);
  396.         EraseRect(scoreFace^.iconMask.bounds);
  397.         MoveTo(2, 14);
  398.         ForeColor(blackColor);
  399.         DrawString('Score:');
  400.         SATDrawLong(score);
  401.         ForeColor(whiteColor);
  402.         MoveTo(0, 12);
  403.         DrawString('Score:');
  404.         SATDrawLong(score);
  405.         ForeColor(blackColor);
  406.         SATSetPortScreen;
  407.         SATSetPortMask(scoreFace);
  408.         EraseRect(scoreFace^.iconMask.bounds);
  409.         MoveTo(0, 12);
  410.         DrawString('Score:');
  411.         SATDrawLong(score);
  412.         MoveTo(2, 14);
  413.         DrawString('Score:');
  414.         SATDrawLong(score);
  415.         SATSetPortScreen;
  416.         SATChangedFace(scoreFace);
  417.     end;
  418.  
  419.     procedure RedrawHighFace;
  420.         var
  421.             str: Str255;
  422.     begin
  423.         str := stringof('High score:', MyNumToString(settings^^.high), ' by ', settings^^.player);
  424.  
  425.         SATSetPortFace(highFace);
  426.         EraseRect(highFace^.iconMask.bounds);
  427.         MoveTo(2, 14);
  428.         ForeColor(blackColor);
  429.         DrawString(str);
  430.         ForeColor(whiteColor);
  431.         MoveTo(0, 12);
  432.         DrawString(str);
  433.         ForeColor(blackColor);
  434.         SATSetPortScreen;
  435.         SATSetPortMask(highFace);
  436.         EraseRect(highFace^.iconMask.bounds);
  437.         MoveTo(0, 12);
  438.         DrawString(str);
  439.         MoveTo(2, 14);
  440.         DrawString(str);
  441.         SATSetPortScreen;
  442.         SATChangedFace(highFace);
  443.     end;
  444.  
  445.     procedure RedrawLastFace;
  446.     begin
  447.         SATSetPortFace(lastface);
  448.         EraseRect(lastface^.iconMask.bounds);
  449.         MoveTo(2, 14);
  450.         DrawString('Last score:');
  451.         SATDrawLong(score);
  452.         ForeColor(whiteColor);
  453.         MoveTo(0, 12);
  454.         DrawString('Last score:');
  455.         SATDrawLong(score);
  456.         ForeColor(blackColor);
  457.         SATSetPortScreen;
  458.         SATSetPortMask(lastface);
  459.         EraseRect(lastface^.iconMask.bounds);
  460.         MoveTo(0, 12);
  461.         DrawString('Last score:');
  462.         SATDrawLong(score);
  463.         MoveTo(2, 14);
  464.         DrawString('Last score:');
  465.         SATDrawLong(score);
  466.         SATSetPortScreen;
  467.         SATChangedFace(lastface);
  468.     end;
  469.  
  470.     var
  471.         playerFace: array[0..15] of FacePtr;
  472.         playerSpeed: array[0..15] of Point;
  473.  
  474.  
  475.  
  476.  
  477.  
  478. {Redraw all player faces. This is separated from InitPlayerFaces since it must be called on}
  479. {depth changes.}
  480.     procedure ReDrawPlayerFaces;
  481.         const
  482.             totalAngle = 240;
  483.         var
  484.             i: integer;
  485.             r, r1, r2, ri: Rect;
  486.             reg1, reg2: RgnHandle;
  487.             pol: PolyHandle;
  488.     begin
  489.         SetRect(r, 0, 0, 40, 40); {Total face size}
  490.         SetRect(r1, 0, 0, 38, 38); {Colored part}
  491.         SetRect(ri, 9, 9, 29, 29); {Colored part, inner circle}
  492.         SetRect(r2, 2, 2, 40, 40); {Shadow}
  493.         for i := 0 to 15 do
  494.             begin
  495.                 reg1 := NewRgn;
  496.                 reg2 := NewRgn;
  497.  
  498. {Generate shape}
  499.                 SATSetPortMask(playerFace[i]);
  500.                 PaintArc(r1, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle);
  501.                 EraseArc(ri, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle); {360-graders-skala}
  502. {$IFC GENERATINGPOWERPC }
  503.                 if noErr <> BitMapToRegion(reg1, playerFace[i]^.iconMask) then{}
  504.                     ;
  505. {$ELSEC}
  506.                 if noErr <> BitMapToRegionGlue(reg1, playerFace[i]^.iconMask) then{}
  507.                     ;
  508. {$ENDC}
  509.                 CopyRgn(reg1, reg2);
  510.                 OffsetRgn(reg2, 2, 2);
  511.  
  512. {Draw face}
  513.                 SATSetPortFace(playerFace[i]);
  514.                 EraseRect(playerFace[i]^.iconMask.bounds);
  515.                 ForeColor(blackColor);
  516.                 PaintRgn(reg2); {black "Shadow"}
  517.                 ForeColor(cyanColor);
  518.                 if gSAT.initDepth > 1 then
  519.                     PaintRgn(reg1) {If we run in color, fill it completely with cyan}
  520.                 else
  521. {$IFC UNDEFINED THINK_PASCAL}
  522.                     FillRgn(reg1, qd.ltGray); {If we run in b/w, a gray pattern looks nicer}
  523. {$ELSEC}
  524.                 FillRgn(reg1, ltGray); {If we run in b/w, a gray pattern looks nicer}
  525. {$ENDC}
  526.                 ForeColor(blueColor);
  527.                 FrameRgn(reg1);
  528.                 ForeColor(blackColor);
  529. {Draw mask}
  530.                 SATSetPortMask(playerFace[i]);
  531.                 EraseRect(playerFace[i]^.iconMask.bounds);
  532.                 PaintRgn(reg1);
  533.                 PaintRgn(reg2);
  534.                 SATSetPortScreen;
  535.                 SATChangedFace(playerFace[i]);
  536.  
  537.                 DisposeRgn(reg1);
  538.                 DisposeRgn(reg2);
  539.             end;
  540.     end;
  541.  
  542. {Create all player faces.}
  543.     procedure InitPlayerFaces;
  544.         var
  545.             i: integer;
  546.             r: Rect;
  547.     begin
  548. {We use crude approximations to the sine/cosine functions we really want.}
  549. {A real game might init the table by using sine and cosine for real, but I don't}
  550. {want to make this harder to read than it already is. A real game would also}
  551. {use more than 16 directions, say 32 or even 64.}
  552.  
  553.         SetPt(playerSpeed[6], 0, -6);
  554.         SetPt(playerSpeed[7], 2, -5);
  555.         SetPt(playerSpeed[8], 4, -4);
  556.         SetPt(playerSpeed[9], 5, -2);
  557.         SetPt(playerSpeed[10], 6, 0);
  558.         SetPt(playerSpeed[11], 5, 2);
  559.         SetPt(playerSpeed[12], 4, 4);
  560.         SetPt(playerSpeed[13], 2, 5);
  561.         SetPt(playerSpeed[14], 0, 6);
  562.         SetPt(playerSpeed[15], -2, 5);
  563.         SetPt(playerSpeed[0], -4, 4);
  564.         SetPt(playerSpeed[1], -5, 2);
  565.         SetPt(playerSpeed[2], -6, 0);
  566.         SetPt(playerSpeed[3], -5, -2);
  567.         SetPt(playerSpeed[4], -4, -4);
  568.         SetPt(playerSpeed[5], -2, -5);
  569.  
  570.         SetPt(playerSpeed[6], 0, -32);
  571.         SetPt(playerSpeed[7], 14, -28);
  572.         SetPt(playerSpeed[8], 22, -22);
  573.         SetPt(playerSpeed[9], 28, -14);
  574.         SetPt(playerSpeed[10], 32, 0);
  575.         SetPt(playerSpeed[11], 28, 14);
  576.         SetPt(playerSpeed[12], 22, 22);
  577.         SetPt(playerSpeed[13], 14, 28);
  578.         SetPt(playerSpeed[14], 0, 32);
  579.         SetPt(playerSpeed[15], -14, 28);
  580.         SetPt(playerSpeed[0], -22, 22);
  581.         SetPt(playerSpeed[1], -28, 14);
  582.         SetPt(playerSpeed[2], -32, 0);
  583.         SetPt(playerSpeed[3], -28, -14);
  584.         SetPt(playerSpeed[4], -22, -22);
  585.         SetPt(playerSpeed[5], -14, -28);
  586.  
  587.         SetRect(r, 0, 0, 40, 40); {Total face size}
  588.         for i := 0 to 15 do
  589.             begin
  590.                 playerFace[i] := SATNewFace(r);
  591.                 SATChangedFace(playerFace[i]);
  592.             end;
  593.         RedrawPlayerFaces;
  594.     end;
  595.  
  596.     procedure HandlePlayer (me: C3SpritePtr);
  597.     begin
  598.         me^.mode := gMode;
  599.         me^.face := playerFace[me^.mode];
  600.  
  601.         me^.fixedPos.h := me^.fixedPos.h + playerSpeed[me^.mode].h;
  602.         me^.fixedPos.v := me^.fixedPos.v + playerSpeed[me^.mode].v;
  603.  
  604.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  605.         me^.position.v := BSR(me^.fixedPos.v, 4);
  606.  
  607.         if me^.fixedPos.h < 0 then
  608.             begin
  609.                 me^.position.h := 0;
  610.                 me^.fixedPos.h := 0;
  611. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  612.             end;
  613.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  614.             begin
  615.                 me^.position.h := gSAT.offSizeH - me^.hotRect.right;
  616.                 me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  617. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  618.             end;
  619.         if me^.fixedPos.v < 0 then
  620.             begin
  621.                 me^.position.v := 0;
  622.                 me^.fixedPos.v := 0;
  623. {gMode := BitAnd(-gMode, 15);}
  624.             end;
  625.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  626.             begin
  627.                 me^.position.v := gSAT.offSizeV - me^.hotRect.bottom;
  628.                 me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  629. {gMode := BitAnd(-gMode, 15);}
  630.             end;
  631.     end;
  632.  
  633. {Get a vector from center to center of two sprites}
  634.     function Vector (s1, s2: SpritePtr): Point;
  635.     begin
  636.         Vector.h := s1^.position.h + s1^.face^.iconMask.bounds.right div 2 - s2^.position.h - s2^.face^.iconMask.bounds.right div 2;
  637.         Vector.v := s1^.position.v + s1^.face^.iconMask.bounds.right div 2 - s2^.position.v - s2^.face^.iconMask.bounds.right div 2;
  638.     end;
  639.  
  640. {Squared distance between centers of two sprites}
  641.     function Dist2 (s1, s2: SpritePtr): Longint;
  642.         var
  643.             v: Point;
  644.     begin
  645.         v := Vector(s1, s2);
  646.         Dist2 := v.h * v.h + v.v * v.v;
  647.     end;
  648.  
  649.     procedure CreatePill;
  650.     forward;
  651.  
  652.  
  653. {***Check for hits based on regions - reuseable procedure!***}
  654.     function RegionHitTest (s1, s2: SpritePtr): Boolean;
  655.         var
  656.             r1, r2: RgnHandle;
  657.     begin
  658. {We know that out hotRects coincide. However, that doesn't mean that we must take it as a}
  659. {collision! Rather, we can do more processing here to decide whether or not it was a collision.}
  660. {In this case, we copy the mask regions of each sprite, offset them to the proper positions,}
  661. {and check if they, too, overlap!}
  662. {}
  663. {Do you think we are doing double work, both dealing with hotRects and the regions? If you do,}
  664. {let me explain some more. The idea is that SAT checks the hotRects for you, which takes away}
  665. {next to all false hits. Checking hotRects is *fast*, so that's what we can afford to do all-to-all}
  666. {(or all-to-near, depending on the chosen search mode). Once a *possible* collision is detected,}
  667. {we can spend some time analyzing it further!}
  668.  
  669. {First of all, let's do some error checking. We could also have done this when loading the faces.}
  670. {Most programs won't have to bother whether or not the regions have been generated}
  671. {successfully, but when using them this way, they must exist or we may get a crash.}
  672.         if (s1^.face^.maskRgn = nil) or (s2^.face^.maskRgn = nil) then
  673.             begin
  674.                 SATReportStr('Error: No mask region!');
  675.                 exit(RegionHitTest);
  676.             end;
  677.  
  678. {Make copies of the mask regions and offset them to the proper places.}
  679.         r1 := NewRgn;
  680.         r2 := NewRgn;
  681.         CopyRgn(s1^.face^.maskRgn, r1);
  682.         CopyRgn(s2^.face^.maskRgn, r2);
  683.         OffsetRgn(r1, s1^.position.h, s1^.position.v);
  684.         OffsetRgn(r2, s2^.position.h, s2^.position.v);
  685.  
  686.         SectRgn(r1, r2, r1);                    {Is there any overlap?}
  687.  
  688. {If empty, no collision, otherwise, handle the collision!}
  689.         RegionHitTest := not EmptyRgn(r1);
  690.  
  691.         DisposeRgn(r1);
  692.         DisposeRgn(r2);
  693.     end;
  694.  
  695. {Collision handling for the player sprite}
  696.     procedure HitPlayer (me, him: SpritePtr);
  697.         var
  698.             v: Point;
  699.     begin
  700.         if RegionHitTest(me, him) then {Do the sprites *really* overlap?}
  701.             begin
  702.  
  703.                 if Dist2(me, him) > 60 then
  704.                     begin
  705. {Hit too far out, so let's call it the outside. Bounce away him.}
  706. {We could make more efforts here for a good bounce.}
  707.                         him^.position.h := him^.position.h + me^.speed.h;
  708.                         him^.speed.h := -him^.speed.h + me^.speed.h;
  709.                         him^.position.v := him^.position.v + me^.speed.v;
  710.                         him^.speed.v := -him^.speed.v + me^.speed.v;
  711. {Finally, make sure the other is moving *away* from us!}
  712. {And when we're at it, why not move it just a little, too?}
  713. {Yuck, this is ugly! Yup, careless programming. Hack, hack!}
  714.                         v := Vector(me, him);
  715.                         if v.h > 0 then
  716.                             begin
  717.                                 if him^.speed.h > 0 then
  718.                                     him^.speed.h := -him^.speed.h;
  719.                                 him^.position.h := him^.position.h - 1;
  720.                             end
  721.                         else
  722.                             begin
  723.                                 if v.h < 0 then
  724.                                     if him^.speed.h < 0 then
  725.                                         him^.speed.h := -him^.speed.h;
  726.                                 him^.position.h := him^.position.h + 1;
  727.                             end;
  728.                         if v.v > 0 then
  729.                             begin
  730.                                 if him^.speed.v > 0 then
  731.                                     him^.speed.v := -him^.speed.v;
  732.                                 him^.position.v := him^.position.v - 1;
  733.                             end
  734.                         else
  735.                             begin
  736.                                 if v.v < 0 then
  737.                                     if him^.speed.v < 0 then
  738.                                         him^.speed.v := -him^.speed.v;
  739.                                 him^.position.v := him^.position.v + 1;
  740.                             end;
  741.  
  742.                     end
  743.                 else
  744.                     begin
  745. {This looks like inside! Let's eat him.}
  746.                         score := score + 1;
  747.                         RedrawScoreFace;
  748.                         him^.task := nil;
  749.                         setCount := setCount - 1;
  750.                         if setCount < 2 then
  751.                             CreatePill; {There should always be pills left!}
  752.                         SATSoundPlay(bippH, 1, true);
  753.                     end; {Dist2}
  754.             end; {RegionHitTest}
  755.  
  756.     end;
  757.  
  758. {Create the score face}
  759.     procedure InitScoreFace;
  760.         var
  761.             r: Rect;
  762.     begin
  763.         SetRect(r, 0, 0, 80, 14);{}
  764.         scoreFace := SATNewFace(r);
  765.         SATChangedFace(scoreFace);
  766.         SetRect(r, 0, 0, 200, 16);{}
  767.         highFace := SATNewFace(r);
  768.         SATChangedFace(highFace);
  769.         SetRect(r, 0, 0, 120, 16);{}
  770.         lastFace := SATNewFace(r);
  771.         SATChangedFace(lastFace);
  772.     end;
  773.  
  774.     procedure SetupDummy (me: SpritePtr);
  775.     begin
  776.         me^.task := @SATBounce;
  777.     end;
  778.  
  779.     procedure SetupSmall (me: C3SpritePtr);
  780.     begin
  781.         me^.face := myFace;
  782.         me^.hotRect := me^.face^.iconMask.bounds;
  783.         me^.task := @HandleTheSprite;
  784.  
  785.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  786.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  787.     end;
  788.  
  789.     procedure SetupPlayer (me: C3SpritePtr);
  790.     begin
  791.         me^.face := playerFace[0];
  792.         me^.hotRect := me^.face^.iconMask.bounds;
  793.         me^.task := @HandlePlayer;
  794.         me^.hitTask := @HitPlayer;
  795.  
  796.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  797.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  798.     end;
  799.  
  800.  
  801.     procedure CreatePill;
  802.         var
  803.             sp: SpritePtr;
  804.     begin
  805.         sp := SATNewSprite(-1, SATRand(gSAT.offSizeH - 32), SATRand(gSAT.offSizeV - 32), @SetupSmall);
  806.         setCount := setCount + 1; {Number of active pills}
  807.     end;
  808.  
  809.  
  810.     procedure NewSet;
  811.         var
  812.             sp: SpritePtr;
  813.             i: integer;
  814.     begin
  815. {Kill all sprites}
  816.         while gSAT.sRoot <> nil do
  817.             SATKillSprite(gSAT.sRoot);
  818.  
  819. {Create the pills}
  820.         for i := 1 to 10 do
  821.             CreatePill;
  822.         if settings^^.high > 7 then
  823.             for i := 8 to settings^^.high do
  824.                 CreatePill;
  825.  
  826.         sp := SATNewSprite(0, SATRand(gSAT.offSizeH - 32), SATRand(gSAT.offSizeV - 32), @SetupDummy);
  827.         RedrawScoreFace;
  828.         sp^.face := scoreFace;
  829.         repeat
  830.             sp^.speed.h := SATRand(5) - 2
  831.         until sp^.speed.h <> 0;
  832.         repeat
  833.             sp^.speed.v := SATRand(3) - 1
  834.         until sp^.speed.v <> 0;
  835.         sp^.hotRect := sp^.face^.iconMask.bounds;
  836. {Hoppsan- fattas nåt!}
  837.  
  838.         sp := SATNewSprite(1, (gSAT.offSizeH - 32) div 2, (gSAT.offSizeV - 32) div 2, @SetupPlayer);
  839.         gMode := 0;
  840.  
  841.         SATBackChanged(gSAT.bounds);
  842.         FlushEvents(6, 0); {Glöm klick från förra uppsättningen!}
  843.         if not (TickCount > gameStartTime + kGameTime) then {Om tiden INTE är ute så skall vi ändra!}
  844.             lastSetStartTime := TickCount;
  845.     end;
  846.  
  847. {An example of how you can (with some effort) scale a sprite.}
  848.     procedure ScaleWelcomeFace;
  849.         var
  850.             srcFacePort, destFacePort: GrafPtr;
  851.             i: integer;
  852.             scaleRect: Rect;
  853.     begin
  854. {Get the rectangle of the original}
  855.         scaleRect := welcomeFace^.iconMask.bounds;
  856.  
  857.         for i := 0 to 31 do
  858.             begin
  859. {SetPortFace to the source. This must be done each turn since ChangedFace changes it.}
  860.                 SATSetPortFace(welcomeFace); {Set the FIRST of SAT's two internal face-ports to the original face.}
  861.                 GetPort(srcFacePort); {Get the port.}
  862. {Modify the size}
  863.                 scaleRect.bottom := scaleRect.bottom - 2;
  864.                 scaleRect.right := scaleRect.right - 2;
  865. {Create the new face}
  866.                 if scaledFace[i] = nil then
  867.                     scaledFace[i] := SATNewFace(scaleRect);
  868. {Get a port to it}
  869.                 SATSetPortFace2(scaledFace[i]); {Set the SECOND of SAT's two internal face ports to the new face.}
  870.                 GetPort(destFacePort); {Get the port.}
  871. {Copy the image}
  872.                 CopyBits(srcFacePort^.portBits, destFacePort^.portBits, welcomeFace^.iconMask.bounds, scaleRect, srcCopy + ditherCopy, nil);
  873.                 CopyBits(welcomeFace^.iconMask, scaledFace[i]^.iconMask, welcomeFace^.iconMask.bounds, scaleRect, srcCopy, nil);
  874.                 SATChangedFace(scaledFace[i]); {Done changing it. Tell SAT that it may do whatever it needs.}
  875.             end; {for}
  876.     end; {ScaleWelcomeFace}
  877.  
  878.     procedure WindUpdate (whatever: Boolean);
  879.         var
  880.             savePort: GrafPtr;
  881.             saveDev: GDHandle;
  882.     begin
  883.         if SATDepthChangeTest then
  884. {IMPORTANT! We must redraw all internally generated faces on depth changes!}
  885.             begin
  886.                 ReDrawPlayerFaces;
  887.                 RedrawScoreFace;
  888.                 RedrawHighFace;
  889.                 RedrawLastFace;
  890.                 ScaleWelcomeFace;
  891.  
  892. {We also have to redraw the background, since it's not a PICT (in which case that is automatic)}
  893.                 GetPort(savePort);
  894.                 if gSAT.colorFlag then
  895.                     saveDev := GetGDevice;
  896.                 SATSetPortBackScreen;
  897.                 SATPenPat(bgPat);
  898.                 PaintRect(gSAT.backScreen.port^.portRect);
  899.                 PenNormal;
  900.                 CopyBits(gSAT.backScreen.port^.portBits, gSAT.offScreen.port^.portBits, gSAT.offScreen.port^.portRect, gSAT.offScreen.port^.portRect, srcCopy, nil);
  901.                 SetPort(savePort);
  902.                 if gSAT.colorFlag then
  903.                     SetGDevice(saveDev);
  904.  
  905.             end;
  906.         SATRedraw;
  907.     end;
  908.  
  909.     procedure WindClose;
  910.     begin
  911.         SkelWhoa;
  912.     end;
  913.  
  914.     procedure WindMouse (where: Point; when: Longint; modifiers: integer);
  915.         var
  916.             found, sp: SpritePtr;
  917.             anyLeft: Boolean;
  918.             myRegion: RgnHandle;
  919.     begin
  920. {Not needed for the game, but note that we can check the mask region of a sprite}
  921. {towards a mouse click as well as a colliding sprite! For demonstrating this, mouse}
  922. {clicks are processed, and if a sprite is hit, a SysBeep is made. Try this by clicking}
  923. {in and around the "Hello" sprite!}
  924.  
  925.         myRegion := NewRgn;
  926.         sp := gSAT.sRoot;
  927.         found := nil;
  928.         while sp <> nil do                                        {Search through the sprite list}
  929.             begin
  930.                 if PtInRect(where, sp^.r) then                        {We are in the rect!}
  931.                     if sp^.face <> nil then                            {Does it have a face at all? Remember it's legal not to have one!}
  932.                         if sp^.face^.maskRgn <> nil then                {Does that face have a mask region? It should, but…}
  933.                             begin
  934.                                 CopyRgn(sp^.face^.maskRgn, myRegion);    {Copy the mask region}
  935.                                 OffsetRgn(myRegion, sp^.position.h, sp^.position.v);        {Offset it to the position of the sprite}
  936.                                 if PtInRgn(where, myRegion) then            {Are we in the region?}
  937.                                     found := sp;                                    {Yes!}
  938.                             end;
  939.                 sp := sp^.next;                                        {Next sprite…}
  940.             end;
  941.         if found <> nil then
  942.             SysBeep(1);                                            {We hit something. Tell us so.}
  943.         DisposeRgn(myRegion);
  944.     end;
  945.  
  946.  
  947.     procedure WindKey (theKey: char; theMods: integer);
  948.     begin
  949. {Hard-coded keys; real games have customizable keys.}
  950.         case theKey of
  951.             ',', 'z', '1': 
  952.                 gMode := BitAnd(gMode - 1, 15);
  953.             '.', 'x', '2': 
  954.                 gMode := BitAnd(gMode + 1, 15);
  955.             otherwise
  956.         end; {case}
  957.         ObscureCursor; {Hide the cursor until the mouse is moved.}
  958.     end;
  959.  
  960.     procedure SetupSAT (theWind: WindowPtr);{Calls SATCustomInit and paints the background with a pattern}
  961.         var
  962.             savePort: SATPort;
  963.             r: Rect;
  964.     begin
  965.         SATGetPort(savePort);
  966.  
  967.         SetPort(theWind);
  968.         r := theWind^.portRect;
  969.         OffsetRect(r, -r.left, -r.top);
  970.         SATCustomInit(0, 0, r, theWind, nil, false, false, false, true, false); {Nytt försök!}
  971. {SATCustomInit(0, 0, theWind^.portRect, theWind, nil, false, false, false, true, false); {Nytt försök!}
  972.  
  973. {We use a customized sprite record! Thus, we must SetSpriteSize before creating sprites!}
  974.         SATSetSpriteRecSize(sizeof(C3Sprite));
  975.  
  976.         if bgPat = nil then
  977.             bgPat := SATGetPat(128);
  978.         if bgPat = nil then
  979.             Barf;
  980.  
  981.         SATSetPortBackScreen;
  982.         SATPenPat(bgPat);
  983.         PaintRect(gSAT.backScreen.port^.portRect);
  984.         PenNormal;
  985.         CopyBits(gSAT.backScreen.port^.portBits, gSAT.offScreen.port^.portBits, gSAT.offScreen.port^.portRect, gSAT.offScreen.port^.portRect, srcCopy, nil);
  986.  
  987.         SATSetPort(savePort);
  988.         CopyBits(gSAT.backScreen.port^.portBits, gSAT.wind.port^.portBits, gSAT.wind.port^.portRect, gSAT.wind.port^.portRect, srcCopy, nil);
  989.  
  990.         if SkelWindow(theWind, @WindMouse, @WindKey, @WindUpdate, nil, @WindClose, nil, nil, false) then
  991.             ;
  992.  
  993.     end;
  994.  
  995.     procedure SetupWindow;
  996.         var
  997.             slaskWind, theWind: WindowPtr;
  998.             tmpWorld: SysEnvRec;
  999.             tmpCol: Boolean;
  1000. {r: Rect;}
  1001. {peek: WindowPeek;}
  1002.     begin
  1003.         tmpCol := false;
  1004.         if noErr = SysEnvirons(1, tmpWorld) then
  1005.             tmpCol := tmpWorld.hasColorQD;
  1006.  
  1007.         if tmpCol then
  1008.             theWind := GetNewCWindow(theWindRes, nil, WindowPtr(-1))
  1009.         else
  1010.             theWind := GetNewWindow(theWindRes, nil, WindowPtr(-1));
  1011.  
  1012. {peek := WindowPeek(theWind);}
  1013.  
  1014.         if theWind = nil then
  1015.             Barf;
  1016.  
  1017. {MoveWindow(theWind, 50, 50, false);}
  1018.  
  1019. {r := WindowPeek(theWind)^.contRgn^^.rgnBBox;}
  1020.  
  1021. {slaskWind := theWind;}
  1022.  
  1023.         SetupSAT(theWind); {Calls SATCustomInit and paints the background with a pattern}
  1024.  
  1025.         SATSetPortScreen;
  1026.         ShowWindow(gSAT.wind.port);
  1027.         SelectWindow(gSAT.wind.port);
  1028.         SATRedraw;
  1029.     end;
  1030.  
  1031. {The task the welcome sprite has while zooming.}
  1032.     procedure ZoomWelcome (me: SpritePtr);
  1033.     begin
  1034.         me^.mode := me^.mode + 1;
  1035. {Compensate for the size change to make it centered in one place.}
  1036.         me^.position.h := me^.position.h - 1;
  1037.         me^.position.v := me^.position.v - 1;
  1038.         if me^.mode >= 32 then
  1039.             begin
  1040.                 me^.face := welcomeFace;
  1041.                 me^.task := @SATBounce;
  1042.             end
  1043.         else
  1044.             me^.face := scaledFace[32 - me^.mode];
  1045.     end;
  1046.  
  1047. {Initialize faces.}
  1048.     procedure InitSpriteFaces;
  1049.         var
  1050.             i: integer;
  1051.     begin
  1052.         myFace := SATGetFace(128);
  1053.         if myFace = nil then
  1054.             Barf;
  1055.         welcomeFace := SATGetFace(138);
  1056.         if welcomeFace = nil then
  1057.             Barf;
  1058. {We don't HAVE to bail out when a face fails to load - the program will stll wor, but that face will}
  1059. {not be visible.}
  1060.         ScaleWelcomeFace;
  1061.     end;
  1062.  
  1063.     var
  1064.         lastTime: Longint;
  1065.  
  1066. {DirtyWork is called from TransSkel}
  1067.     procedure DirtyWork;
  1068.         var
  1069.             sp: SpritePtr;
  1070.             ph: PicHandle;
  1071.             r: Rect;
  1072.             savePort: GrafPtr;
  1073.             saveDev: GDHandle;
  1074.     begin
  1075. {We can check TickCount as usual, since we never know how often we get null events.}
  1076.         if lastTime + 1 < TickCount then
  1077.             begin
  1078.                 SATRun(false);
  1079.                 lastTime := tickCount;
  1080.             end;
  1081.  
  1082.         if gameRunning then
  1083.             begin
  1084. {Timebar}
  1085.                 GetPort(savePort);
  1086.                 if gSAT.colorFlag then
  1087.                     saveDev := GetGDevice;
  1088.                 SATSetPortBackScreen;
  1089. {I *should* change only the part that actually changes!}
  1090.                 r := gSAT.wind.port^.portRect;
  1091.                 SATBackChanged(r);
  1092.                 r.right := 5;
  1093.                 r.top := r.bottom * (lastSetStartTime + kGameTime - TickCount) div kGameTime;
  1094.                 ForeColor(redColor); {Quickest way to get a color.}
  1095.                 PaintRect(r);
  1096.                 r.bottom := r.top;
  1097.                 r.top := 0;
  1098.                 SATPenPat(bgPat);
  1099.                 PaintRect(r);
  1100.                 PenNormal;
  1101.  
  1102.                 SetPort(savePort);
  1103.                 if gSAT.colorFlag then
  1104.                     SetGDevice(saveDev);
  1105. {end of Timebar}
  1106.  
  1107.                 if TickCount > lastSetStartTime + kGameTime then
  1108.                     begin
  1109.                         SATSoundPlay(baeH, 5, true);
  1110. {NewSet;}
  1111.  
  1112.                         if TickCount > gameStartTime + kGameTime then
  1113.                             begin
  1114.                                 if score > settings^^.high then
  1115.                                     begin
  1116. {settings^^.high := score;}
  1117.                                         SATSoundEvents;
  1118.                                         AskHigh;
  1119.                                         ChangedResource(Handle(settings));
  1120.                                     end;
  1121.  
  1122. {Kill all sprites}
  1123.                                 while gSAT.sRoot <> nil do
  1124.                                     SATKillSprite(gSAT.sRoot);
  1125.  
  1126.                                 RedrawHighFace;
  1127.                                 RedrawLastFace;
  1128. {Time for breaking some of my conventions! The stuff below should be done in "setup" and "handle"}
  1129. {routines, as recommened in the manual and done in other demos - but if we want to mess up the code,}
  1130. {we are free to do so! The sprites below set up their faces and speeds right here, and share a common}
  1131. {handling routine (SATBounce).}
  1132.  
  1133. {Make the "hello" sprite}
  1134.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1135.                                 sp^.face := welcomeFace;
  1136.                                 repeat
  1137.                                     sp^.speed.h := SATRand(3) - 1
  1138.                                 until sp^.speed.h <> 0;
  1139.                                 repeat
  1140.                                     sp^.speed.v := SATRand(3) - 1
  1141.                                 until sp^.speed.v <> 0;
  1142.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1143.                                 sp^.task := @ZoomWelcome;
  1144. {High score sprite}
  1145.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 4, @SetupDummy);
  1146.                                 sp^.face := highFace;
  1147.                                 repeat
  1148.                                     sp^.speed.h := SATRand(7) - 3
  1149.                                 until sp^.speed.h <> 0;
  1150.                                 repeat
  1151.                                     sp^.speed.v := SATRand(3) - 1
  1152.                                 until sp^.speed.v <> 0;
  1153.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1154. {Last score sprite}
  1155.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 3, @SetupDummy);
  1156.                                 sp^.face := lastFace;
  1157.                                 repeat
  1158.                                     sp^.speed.h := SATRand(7) - 3
  1159.                                 until sp^.speed.h <> 0;
  1160.                                 repeat
  1161.                                     sp^.speed.v := SATRand(3) - 1
  1162.                                 until sp^.speed.v <> 0;
  1163.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1164.  
  1165.                                 SATSetPortScreen;
  1166.                                 SATRedraw; {Just to make sure killed sprites are erased}
  1167.                                 gameRunning := false;
  1168.                             end;
  1169.  
  1170.                     end;
  1171.             end;
  1172.  
  1173.         if not gameRunning then
  1174.             if gSAT.sRoot = nil then
  1175.                 begin
  1176. {Messy code for setting up the "hello" sprite - which is why I recommend the use of setup routines.}
  1177.                     sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1178.                     sp^.face := welcomeFace;
  1179.                     repeat
  1180.                         sp^.speed.h := SATRand(3) - 1
  1181.                     until sp^.speed.h <> 0;
  1182.                     repeat
  1183.                         sp^.speed.v := SATRand(3) - 1
  1184.                     until sp^.speed.v <> 0;
  1185.                     sp^.hotRect := sp^.face^.iconMask.bounds;
  1186.                     sp^.task := @ZoomWelcome;
  1187.                 end;
  1188.     end;
  1189.  
  1190.     procedure InitHigh;
  1191.     begin
  1192.         settings := SettingsHnd(GetResource('Sett', 0));
  1193.         if settings = nil then {Didn't exist - create it!}
  1194.             begin
  1195.                 settings := SettingsHnd(NewHandle(Sizeof(SettingsRec)));
  1196.                 if settings = nil then
  1197.                     begin
  1198.                         SysBeep(1);
  1199.                         halt;
  1200.                     end;
  1201.                 settings^^.high := 0;
  1202.                 AddResource(handle(settings), 'Sett', 0, '');
  1203.             end
  1204.         else {Did exist - check the size!}
  1205.             if GetHandleSize(Handle(settings)) < sizeof(SettingsRec) then
  1206.                 SetHandleSize(Handle(settings), sizeof(SettingsRec));
  1207.     end;
  1208.  
  1209.     procedure DoFileMenu (item: integer);
  1210.     begin
  1211.         case item of
  1212.             newGameItem: 
  1213.                 begin
  1214.                     score := 0;
  1215.                     gameRunning := true;
  1216.                     gameStartTime := TickCount;
  1217.                     lastSetStartTime := TickCount;
  1218.                     setCount := 0;
  1219.                     NewSet;
  1220.  
  1221.                     ObscureCursor; {Hide the cursor until the mouse is moved.}
  1222.                 end;
  1223.             clearHighItem: 
  1224.                 if SATQuestionStr('Set the high score to zero?') then
  1225.                     begin
  1226.                         settings^^.high := 0;
  1227.                         ChangedResource(handle(settings));
  1228.                     end;
  1229.             otherwise
  1230.                 SkelWhoa;
  1231.         end;
  1232.     end;
  1233.  
  1234.     procedure DoShapeMenu (item: integer);
  1235.         const
  1236.             wide = 1;
  1237.             tall = 2;
  1238.         var
  1239.             p: Point;
  1240.     begin
  1241.         p := gSAT.wind.port^.portRect.botRight;
  1242.         case item of
  1243.             wide: 
  1244.                 if gSAT.wind.port^.portRect.bottom > gSAT.wind.port^.portRect.right then
  1245.                     begin
  1246.                         CheckItem(shapeMenu, wide, true);
  1247.                         CheckItem(shapeMenu, tall, false);
  1248.                         SizeWindow(gSAT.wind.port, p.v, p.h, false); {swap size}
  1249.                         SATKill;
  1250.                         SetupSAT(gSAT.wind.port);
  1251.                         gameRunning := false;
  1252.                     end;
  1253.             tall: 
  1254.                 if gSAT.wind.port^.portRect.bottom < gSAT.wind.port^.portRect.right then
  1255.                     begin
  1256.                         CheckItem(shapeMenu, tall, true);
  1257.                         CheckItem(shapeMenu, wide, false);
  1258.                         SizeWindow(gSAT.wind.port, p.v, p.h, false); {swap size}
  1259.                         SATKill;
  1260.                         SetupSAT(gSAT.wind.port);
  1261.                         gameRunning := false;
  1262.                     end;
  1263.             otherwise
  1264.                 SysBeep(1);
  1265.         end;{case}
  1266.     end;
  1267.  
  1268.     procedure SetUpMenus;
  1269.     begin
  1270.         SkelApple('About CollisionIII…', @DoAbout);
  1271.         fileMenu := GetMenu(fileMenuRes);
  1272.         if fileMenu = nil then
  1273.             Barf;
  1274.         if SkelMenu(fileMenu, @DoFileMenu, nil, true) then
  1275.             ;
  1276.         shapeMenu := GetMenu(shapeMenuRes);
  1277.         if shapeMenu = nil then
  1278.             Barf;
  1279.         if SkelHMenu(shapeMenu, @DoShapeMenu, nil) then {Install as hierarcical menu}
  1280.             ;
  1281.         CheckItem(shapeMenu, 1, true); {Check "wide"}
  1282.     end;
  1283.  
  1284. begin
  1285.     SkelInit(6, nil);
  1286.     SkelSetSleep(0); {Tell TransSkel that we want attention as often as possible.}
  1287.     SetupMenus;
  1288.     SetupWindow;
  1289.  
  1290.     InitHigh;
  1291.     InitSpriteFaces;
  1292.     InitScoreFace;
  1293.     InitPlayerFaces;
  1294.  
  1295.     SkelBackground(@DirtyWork);
  1296.     lastTime := TickCount;
  1297. {$IFC UNDEFINED THINK_PASCAL}
  1298.     qd.randSeed := TickCount;
  1299. {$ELSEC}
  1300.     randSeed := TickCount;
  1301. {$ENDC}
  1302.  
  1303.     Synth; {Build sounds!}
  1304.     SATSoundShutup;
  1305.  
  1306.     SkelMain;
  1307.     SkelClobber;
  1308.     SATSoundShutup;
  1309. end.